package com.biubiux2;

import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Random;
import java.util.concurrent.*;
import java.util.stream.Stream;

/**
 * 执行命令：
 * chcp 65001
 * java -jar '-Dfile.encoding=utf-8' '-Dn=120' '-Dtimes=1' '-Ddelay=0' .\client-1.0-SNAPSHOT-jar-with-dependencies.jar
 *
 * @author noexcs
 * @since 8/7/2022 5:53 PM
 */
@Slf4j
public class ServerStressTest {

    public static void main(String[] args) {
        // 上线人数
        int N = 500;
        try {
            String n = System.getProperty("n");
            if (n != null) {
                N = Integer.parseInt(n);
            }
        } catch (NumberFormatException e) {
            log.debug("parse property error");
        }
        log.debug("User count:{}", N);
        // 上线uid范围：[lower, upper]
        int lower = 100001;
        int upper = lower + N - 1; // inclusive

        // 每人发送几次
        int times = 50;
        String t = System.getProperty("times");
        if (t != null) {
            try {
                times = Integer.parseInt(t);
            } catch (NumberFormatException e) {
                log.debug("Parse t times error");
            }
        }
        log.debug("每人发送{}条", times);
        // 每次间隔多少毫秒
        int delay = 5000;
        String d = System.getProperty("delay");
        if (d != null) {
            try {
                delay = Integer.parseInt(d);
            } catch (NumberFormatException e) {
                log.debug("parse delay error");
            }
        }
        log.debug("每隔{}毫秒", delay);
        int totalSendCnt = N * times;

        //region 准备工作
        CountDownLatch robotLoginCompletionLatch = new CountDownLatch(N);
        CountDownLatch robotSendCompletionLatch = new CountDownLatch(totalSendCnt);
        int corePoolSize = Runtime.getRuntime().availableProcessors();
        ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, Math.max(corePoolSize, N), 1, TimeUnit.MINUTES, new ArrayBlockingQueue<Runnable>(N));
        ArrayList<Robot> robots = new ArrayList<>(N);
        for (int userId = lower; userId <= upper; userId++) {
            Robot robot = new Robot();
            robot.setSendCompletionLatch(robotSendCompletionLatch);
            robot.setPrivateReceiverLowerLimit(lower);
            robot.setPrivateReceiverUpperLimit(upper);
            robots.add(robot);
            robot.setUserId(userId);
        }
        //endregion

        //region 开始建立链接并登录
        long start = System.currentTimeMillis();
        ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
        int finalN = N;
        service.scheduleAtFixedRate(() -> {
            System.out.println("已完成登录：" + (finalN - robotLoginCompletionLatch.getCount()));
        }, 0, 3, TimeUnit.SECONDS);

        for (Robot robot : robots) {
            executor.execute(() -> {
                        robot.connect("localhost", 8808);
                        robot.login(robot.getUserId(), "123456", robotLoginCompletionLatch);
                    }
            );
        }

        assureAllLoggedIn(robotLoginCompletionLatch);
        long end = System.currentTimeMillis();
        double duration = (end - start) / 1000.0;
        System.out.println("登录：");
        System.out.println("共用时：" + duration + "秒");
        System.out.println("平均用时：" + duration / N + "秒");
        System.out.println();
        service.shutdown();
        //endregion


        //region 开始发送广播
        int totalReceive = totalSendCnt * N;
        service = Executors.newSingleThreadScheduledExecutor();
        ScheduledFuture<?> scheduledFuture = service.scheduleAtFixedRate(() -> {
            long cnt = totalSendCnt - robotSendCompletionLatch.getCount();
            System.out.println();
            System.out.println("已完成发送：" + cnt + "(" + ((double) cnt / totalSendCnt * 100) + "%)");
            int i = Robot.totalBroadcastReceived.intValue();
            System.out.println("已接收" + i + "(" + ((double) i / totalReceive * 100) + "%)");
            System.out.println();
        }, 0, 3, TimeUnit.SECONDS);

        start = System.currentTimeMillis();
        Random random = new Random();
        for (Robot robot : robots) {
            service.scheduleWithFixedDelay(() -> robot.sendOneBroadcast("这是一条广播"),
                    random.nextInt(delay / 1000), delay, TimeUnit.MILLISECONDS);
        }

        while (Robot.totalBroadcastReceived.intValue() < totalReceive) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        scheduledFuture.cancel(false);
        end = System.currentTimeMillis();
        duration = (end - start) / 1000.0;
        System.out.println("发送与接受：");
        System.out.println("共用时：" + duration + "秒");
        System.out.println("平均用时：" + duration / N + "秒");
        System.out.println();
        //endregion


        //region 统计结果
        long msgBroadcastSendCnt = 0;
        long msgPrivateSendCnt = 0;
        long msgPrivateReceivedCnt = 0;
        long msgBroadcastReceivedCnt = 0;
        long completionCnt = 0;

        for (Robot robot : robots) {
            msgBroadcastSendCnt += robot.msgBroadcastSendCnt.intValue();
            int intValue = robot.msgBroadcastReceivedCnt.intValue();
            if (intValue == totalSendCnt) {
                completionCnt++;
            }
            msgBroadcastReceivedCnt += intValue;

            msgPrivateSendCnt += robot.msgPrivateSendCnt.intValue();
            msgPrivateReceivedCnt += robot.msgPrivateReceivedCnt.intValue();
        }


        System.out.println("共发送广播:" + msgBroadcastSendCnt + "条");
        System.out.println("共接收广播:" + msgBroadcastReceivedCnt + "条(" + N + "*" + N + "*" + times + ")");
        System.out.println("共" + completionCnt + "人接收到全部消息");
        System.out.println();
        System.out.println("共发送私信:" + msgPrivateSendCnt + "条");
        System.out.println("共接收私信:" + msgPrivateReceivedCnt + "条");
        //endregion

        for (Robot robot : robots) {
            executor.execute(() -> {
                        robot.logout();
                    }
            );
        }

        try {
            executor.awaitTermination(10, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.exit(0);
    }

    private static void assureAllSendComplete(CountDownLatch robotSendCompletionLatch) {
        try {
            robotSendCompletionLatch.await();
            System.out.println("消息已全部发出");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    private static void assureAllLoggedIn(CountDownLatch robotLoginCompletionLatch) {
        try {
            robotLoginCompletionLatch.await();
            System.out.println("已全部登录");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

}
